<template>
  <div>
    <h1>自定义指令</h1>
    <section>
      <!-- 自定义指令控制按钮的权限 -->
      <button v-has2="'add'">添加</button>
      <button v-has2="'edit'">编辑</button>
      <button v-has2="'delete'">删除</button>
      <!-- 自定义指令控制组件的权限 -->
      <SonTest v-has2="'component1'"></SonTest>

      <!-- 自定义指令例子： 实现input框插入dom的时候立即聚焦和背景颜色变红 -->
      <input id="intt" ref='int' v-focus:int.a.b='1+1'/>
      <!-- 动态参数自定义指令 -->
      <div id="dynamicexample">
        <p v-pin:[direction]="30"> 我是一个动态参数自定义指令，根据指令参数的值定义定位方向，表达式的值定义偏移值</p>
      </div><hr>
    </section>

<!-- ========================================================================================================================= -->
<!-- ========================================================================================================================= -->

    <h1>内置指令</h1>
      <!-- 没有参数的指令：   v-text v-html    v-if v-show      v-for     v-once v-memo    v-pre v-cloak -->
      <!-- 有参数的指令：     v-bind v-on     v-model    v-slot -->

    <!-- ————————————————————————————————————— v-text v-html ————————————————————————————————————————- -->
    <!-- ————————————————————————————————————— v-text v-html ————————————————————————————————————————- -->
    <section>
      v-text v-html
      <div v-text="text"></div>
      <div v-html="html"></div>
    </section>
    <hr />

    <!-- ————————————————————————————————————— v-pre v-cloak ————————————————————————————————————————- -->
    <!-- ————————————————————————————————————— v-once v-memol ————————————————————————————————————————- -->
    <section>
      <!-- 跳过这个元素和它的子元素的编译过程。显示原始 Mustache 标签。跳过大量没有指令的节点会加快编译 -->
      <span v-pre >v-pre指令:{{ pre }}</span>
      <!-- 这个指令保持在元素上直到关联实例结束编译。和 CSS 规则如 [v-cloak] { display: none } 一起用时，
           这个指令可以隐藏未编译的 Mustache 标签直到实例准备完毕。 -->
      <div v-cloak >{{ cloak }}</div>
      <!-- 只渲染元素或组件一次。随后的重新渲染，元素/组件及其所有的子节点将被视为静态内容并跳过 -->
      <div v-once @click="onceFn">v-once指令:元素和组件只渲染一次。{{ once }}</div>
    </section>
    <hr />

    <!-- ————————————————————————————————————— v-bind v-on ————————————————————————————————————————- -->
    <!-- ————————————————————————————————————— v-bind v-on ————————————————————————————————————————- -->
    <section>
      v-bind指令：
      <!-- 绑定一个属性 -->
      <!-- <a v-bind:href="url">v-bind指令:绑定一个属性</a> -->
      <!-- 动态参数 (2.6.0+) -->
      <!-- <a :[key]="url">v-bind指令:动态参数 (2.6.0+)</a> -->
      <!-- 内联字符串拼接 -->
      <!-- <a :[key]="'https://' + url">v-bind指令:内联字符串拼接</a> -->
      <!-- 布尔 attribute (2.6.0+) -->
        <!-- <button v-bind:disabled="isButtonDisabled">v-bind指令:布尔 attribute (2.6.0+)</button> -->
      <!-- 动态 attribute 名 (2.6.0+) -->
      <!-- <button v-bind="{ id: someProp, 'other-attr': otherProp }">v-bind指令:动态 attribute 名 (2.6.0+)</button> -->

      <!-- 通过 $props 将父组件的 props 一起传给子组件 -->
      <SonTest v-bind="$props"></SonTest>
      
      <!-- .sync修饰符：一个语法糖，在绑定的prop的同时将prop名作为所监听的事件，用this.$emit(undate:propName,newValue) 触发 -->
      <SonTest v-bind:bindSync.sync="bindSync"></SonTest>
      <!-- <SonTest
        v-bind:bindSync="bindSync"
        v-on:update:bindSync="(val)=> {bindSync = val}"
      ></SonTest> -->
    </section>
    <hr />
    <!-- ————————————————————————————————————— v-bind v-on ————————————————————————————————————————- -->
    <!-- ————————————————————————————————————— v-bind v-on ————————————————————————————————————————- -->
    <section>
      v-on指令：
      <!-- 方法处理器 -->
      <button v-on:click="onFn($event, 'hello')">方法处理器</button>
      <button v-on:click="()=>{console.log('hello')}">方法处理器</button>
      <button v-on:click="eventType='dbclick'">方法处理器(动态事件变成双击事件)</button>
      <!-- 动态事件 (2.6.0+) : 在触发多种类型的事件时调用同一个方法时使用-->
      <button v-on:[eventType]="onFn">动态事件 (2.6.0+)</button>
      <!-- 对象语法 (2.4.0+) ：同时注册监听多个事件 -->
      <!-- <button v-on="{ mousedown: doThis, mouseup: doThat }"></button> -->
      <!-- 自定义事件： -->

      <!-- 事件修饰符：.stop .prevent .capture .self .once -->
      <!-- 停止冒泡 -->
      <button @click.stop="onFn">停止冒泡(.stop)</button>
      <!-- 阻止默认行为 -->
      <button @click.prevent="onFn">阻止默认行为(.prevent)</button>
      <!--  串联修饰符 -->
      <button @click.stop.prevent="onFn">串联修饰符(.stop.prevent)</button>
      <!-- 点击回调只会触发一次 -->
      <button v-on:click.once="onFn">点击回调只会触发一次(.once)</button>
      <!-- 添加事件监听器时使用事件捕获模式 -->
      <button @click.capture="onFn">添加事件监听器时使用事件捕获模式(.capture)</button>
      <!-- 只当事件在该元素本身（而不是子元素）触发时触发回调 -->
      <button @click.self="onFn">只当事件在该元素本身（而不是子元素）触发时触发回调(.self)</button>
      <!-- 不想阻止事件默认行为。不要把.passive和.prevent一起使用 -->
      <button @click.passive="onFn">不想阻止事件默认行为(.passive)</button>
      <!-- `.native` - 监听组件根元素的原生事件。 -->
      <!-- <SonTest @click.native="onFn">监听组件根元素的原生事件(.native) </SonTest> -->
    </section>
    <hr />


    <!-- ————————————————————————————————————— v-model ————————————————————————————————————————- -->
    <!-- ————————————————————————————————————— v-model ————————————————————————————————————————- -->
    <section>
        v-model指令：
      <!-- 单选框上面使用： -->
      <input type="radio" id="one" name="vmodel" checked="One" value="one"  v-model="radio"/>
      <label for="one">One</label>
      <input type="radio" id="two" name="vmodel" checked="Two" value="two"  v-model="radio"/>
      <label for="two">Two</label>&nbsp;&nbsp;&nbsp;         
      <span>Picked: {{ radio }}</span>&nbsp;&nbsp;&nbsp; 
      <button @click="modelFn">v-model指令：</button>

      <!-- 复选框上使用： -->

      <!-- 选择框上使用： -->

      <!-- v-model在组件上使用： -->
      <div>
        <SonTest v-model="modelVal" @input="changeValToSonTest"></SonTest>
        父组件中的值：{{ modelVal }}
        <!-- <button @click="changeValToSonTest">改变通过v-model传给子组件的值</button> -->
      </div>
    </section>
    <hr />

    <!-- ————————————————————————————————————— v-if v-show ———————————————————————————————————————————- -->
    <!-- ————————————————————————————————————— v-if v-show ———————————————————————————————————————————- -->
    <!-- 根据表达式的值的 truthiness 来有条件地渲染元素。在切换时元素及它的数据绑定 / 组件被销毁并重建。
          如果元素是 <template>，将提出它的内容作为条件块。 
    -->
    <section>
      <!-- 两种方法功能相同 -->
      <button @click="ifFn">{{ number ? "yes" : "no" }}</button>
      <button v-if="number">是</button>
      <button v-else>否</button>

      <div v-if="num == 1">111111</div>
      <div v-else-if="num == 2">222222</div>
      <div v-else>333333</div>

      <!--    不可见的包装器元素,最终的渲染结果将不包含 <template> 元素在页面-->
      <template v-if="true">
        <p> template 上的 v-if渲染分组:</p><p>不可见的包装器元素 1</p><p>最终的渲染结果将不包含 template 元素 2</p>
      </template>

      <!-- v-if中使用 key 管理可复用的元素 ：搭配使用key表示告诉Diff算法不要对这个元素进行复用。例如：表单切换: -->
      <button @click="loginType = !loginType">v-if中使用 key 管理可复用的元素</button>
      <template v-if="loginType">
        <label>Username</label>
        <input placeholder="Enter your username" :key="'username'">
      </template>
      <template v-else>
        <label>Email</label>
        <input placeholder="Enter your email address">
      </template>

      
      <!-- v-show 不支持在 <template> 元素上使用，也不能和 v-else 搭配使用。 -->
      <p v-show="ok">v-show：Hello!</p>
    </section>
    <hr />

    <!-- ———————————————————————————————————— v-for ————————————————————————————————————————————- -->
    <!-- ———————————————————————————————————— v-for ————————————————————————————————————————————- -->
    <section>
      <h3>v-for</h3>
      <!-- 遍历数组 -->
      <div v-for="(item, index) in items" :key="index">
        <li>{{ item.msg }}-----{{ index }}</li>
        <li class="divider" role="presentation">v-for遍历数组</li>
      </div>

      <!-- 遍历对象 -->
      <!-- value:属性值，val：属性名， index：属性的索引 -->
      <div v-for="(value, attr, index) in obj" :key="index">
        <li>遍历对象：{{ index }}. {{ attr }}: {{ value }}</li>
      </div>

      <!-- v-for遍历范围值：  表示把模板重复对应次数。 -->
      <div v-for="(item, index) in 2" :key="index">遍历范围值：{{ item }}</div>

      <!-- <template> 上的 v-for -->
      <!-- <ul>
        <template v-for="(item, index) in 2" :key="index">
          <li>{{ item }}</li>
          <li class="divider" role="presentation">第 {{ item }}块的数据</li>
        </template>
      </ul> -->

      <!-- 组件上使用 v-for： 不会自动将任何数据传递给组件，因为组件有自己独立的作用域。需要我们自行通过props传递 -->
      <!-- <Child v-for="(item, index) in vforComponent" :item="item" :index="index" :key="index"></Child> -->
    </section>

  </div>
</template>

<script>
import SonTest from "./SonTest.vue"
export default {
  components: { SonTest},
  data() {
    return {
      direction: "top",             // 自定义指令的值

      text: "<div>v-text的值：不会把<div></div>渲染成DOM元素</div>", // v-text指令    变量的值只是一个字符串文本，而不是HTML片段
      html: "<div>v-html的值：会把标签字符串渲染成DOM元素</div>", // v-html指令


      pre: "v-pre指令",              // v-pre指令
      cloak: "v-cloak指令",          // v-cloak指令
      once: "元素和组件只渲染一次",   // v-once指令
      memo: "memo",                  // v-memo指令（这个是vue3的指令）


      bindSync: "bindSync",        // v-bind指令 + .sync修饰符
      eventType: "click",          // v-on指---动态事件


      radio: "",          //v-model
      modelVal: "组件上面使用v-model的值",


      num: 1,             // v-if多条件控制切换
      number: false,      // v-if控制切换
      loginType: true,    // v-if中使用 key 管理可复用的元素


      items: [            // v-for遍历数组
        { name: "lisi", msg: "a" },
        { name: "xiaoliu", msg: "b" },
      ],
      obj: {              // v-for遍历对象
        name: "张三",
        age: 15,
      },
      vforComponent:[     // v-for遍历组件
        "第一个组件", "第二个组件"
      ],   

    };
  },

  mounted(){    // 这样子也可以实现 input框插入dom的时候立即聚焦和背景颜色变红
    // document.querySelector("#intt").style.backgroundColor = "green";
    // document.querySelector("#intt").focus()

    // console.log(this.$refs);
    // this.$refs.int.style.backgroundColor = "red";
    // this.$refs.int.focus();
  },

  directives: {
    // 注意：除了 el 之外，其它参数都应该是只读的，切勿进行修改。如果需要在钩子之间共享数据，建议通过元素的 dataset 来进行。
    focus: {
      // 指令第一次绑定到元素时调用, 只调用一次。在这里可以进行一次性的初始化设置。
      bind(el, binding, vnode, oldVnode) {},

      // 指令绑定的元素插入父节点时调用 (仅保证父节点存在，但不一定已被插入文档中)。
      // el:指令所绑定的元素，可以用来直接操作 DOM。      binding:一个对象。包含指令的一些信息
      // vnode：Vue 编译生成的虚拟节点。                 oldVnode：上一个虚拟节点，
      inserted(el, binding, vnode, oldVnode) {
        el.focus();
        el.style.backgroundColor = "red";
        console.log("el:",el);
        /**
         * rawName:   指令全名，包括 v- 前缀, 参数，修饰符。
         * name：指令名，不包括 v- 前缀。
         * value：指令的绑定值，例如：v-my-directive="1 + 1" 中，绑定值为 2。
         * oldValue：指令绑定的前一个值，仅在 update 和 componentUpdated 钩子中可用。无论值是否改变都可用。
         * expression：字符串形式的指令表达式。例如 v-my-directive="1 + 1" 中，表达式为 "1 + 1"。
         * arg：传给指令的参数，可选。例如 v-my-directive:foo 中，参数为 "foo"。
         * modifiers：一个包含修饰符的对象。例如：v-my-directive.foo.bar 中，修饰符对象为 { foo: true, bar: true }。
        */
        console.log("binding:", binding);
        //有一个重要属性 context 上下文对象，可以得到这个节点所在组件的  数据属性方法  等内容
        console.log("vnode:", vnode);   
        // console.log("oldVnode:", oldVnode);  // 在这个钩子函数中不可用
      },

      // 指令所在组件的 VNode 更新时调用，但是可能发生在其子组件 VNode 更新之前。指令的值可能发生了改变，也可能没有。
      update(el, binding, vnode, oldVnode) {
        console.log("update");
      },

      // 指令所在组件的 VNode 及其子组件 VNode 全部更新后调用。
      componentUpdated(el, binding, vnode, oldVnode) {
        console.log("componentUpdated");
      },

      // 指令与元素解绑时调用,只调用一次
      unbind() {
        console.log("unbind");
      },
    },


    // 动态参数自定义指令：其实也可以传给 binding.value 一个对象类型的表达式来一起把定义方向和偏移量来实现这个功能。
    pin: {
      bind: function (el, binding, vnode) {
        el.style.position = 'fixed'
        var s = (binding.arg == 'left' ? 'left' : 'top')
        el.style[s] = binding.value + 'px'
      }
    },

  },

  methods: {
    onceFn() {
      this.once = "数据改变了,v-once指令你干嘛不更新渲染？";
      console.log(this.once);
    },

    onFn(event, val) {
      console.log(event, val);
    },

    modelFn(){
        this.radio = "two"
    },
    changeValToSonTest(val){
      this.modelVal = val
    },

    ifFn(event) {
      // console.log(11);
      console.log(event);
    },
  },
};
</script>

<style>
</style>